#include <math.h>
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <ostream>
#include <random>

#include "buffer/buffer_pool_manager.h"
#include "gtest/gtest.h"
#include "storage/disk/disk_manager_memory.h"
#include "storage/index/b_plus_tree.h"
#include "test_util.h"  // NOLINT

namespace bustub {

using bustub::DiskManagerUnlimitedMemory;

TEST(BPlusTreeTests, IteratorTest) {
  // create KeyComparator and index schema
  auto key_schema = ParseCreateStatement("a bigint");
  GenericComparator<8> comparator(key_schema.get());

  auto disk_manager = std::make_unique<DiskManagerUnlimitedMemory>();
  auto *bpm = new BufferPoolManager(50, disk_manager.get());
  // create and fetch header_page
  page_id_t page_id;
  auto header_page = bpm->NewPage(&page_id);
  // create b+ tree
  BPlusTree<GenericKey<8>, RID, GenericComparator<8>> tree("foo_pk", header_page->GetPageId(), bpm, comparator, 4, 3);
  GenericKey<8> index_key;
  RID rid;
  // create transaction
  auto *transaction = new Transaction(0);

  std::vector<int64_t> keys = {1, 2, 3, 4, 5};
  std::vector<int64_t> insert_keys = {6, 7, 8};
  std::vector<int64_t> final_keys = {1, 2, 3, 4, 5, 6, 7, 8};
  for (auto key : keys) {
    int64_t value = key & 0xFFFFFFFF;
    rid.Set(static_cast<int32_t>(key >> 32), value);
    index_key.SetFromInteger(key);
    tree.Insert(index_key, rid, transaction);
  }

  std::vector<RID> rids;

  size_t i = 0;
  for (auto it = tree.Begin(); it != tree.End(); ++it) {
    ASSERT_LT(i, 5);
    index_key.SetFromInteger(keys[i]);
    int64_t value = keys[i] & 0xFFFFFFFF;
    EXPECT_EQ((*it).second.GetSlotNum(), value);
    EXPECT_EQ(i == keys.size() - 1, it.IsEnd());
    i++;
  }

  for (auto key : insert_keys) {
    int64_t value = key & 0xFFFFFFFF;
    rid.Set(static_cast<int32_t>(key >> 32), value);
    index_key.SetFromInteger(key);
    tree.Insert(index_key, rid, transaction);
  }
  i = 0;
  for (auto it = tree.Begin(); it != tree.End(); ++it) {
    ASSERT_LT(i, keys.size() + insert_keys.size());
    int64_t value = final_keys[i] & 0xFFFFFFFF;
    EXPECT_EQ((*it).second.GetSlotNum(), value);
    EXPECT_EQ(i == keys.size() + insert_keys.size() - 1, it.IsEnd());
    i++;
  }
  std::cout << tree.DrawBPlusTree() << std::endl;

  bpm->UnpinPage(HEADER_PAGE_ID, true);
  delete transaction;
  delete bpm;
}

}  // namespace bustub
